namespace Sagile.Nepma.Forms
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Globalization;
    using System.Reflection;
    using System.Threading;
    using System.Windows.Forms;
    using Properties;

    /// <summary>
    /// Displays a list of all tasks and provide visual information about the current state of the engine.
    /// </summary>
    public partial class MonitoringForm : Form
    {
        #region Internals

        #region Attributes

        private TaskManager _manager;
        private readonly bool _runPerfCounters = true;

        /// <summary>
        /// Stores task id to row index bindings : 
        /// </summary>
        private readonly Dictionary<string, int> _rowBindings = new Dictionary<string, int>();

        /// <summary>
        /// Stores row index bindings to task : 
        /// </summary>
        private readonly Dictionary<int, TaskBase> _reverseRowBindings = new Dictionary<int, TaskBase>();

        private bool _highlightParallels = false;

        #endregion Attributes
        
        #region Methods

        #region Loading

        /// <summary>
        /// Registers the manager.
        /// </summary>
        private void RegisterManager()
        {
            if (null == _manager)
                return;

            _manager.TaskStarted -= _manager_TaskStarted;
            _manager.TaskProgressChanged -= _manager_TaskProgressChanged;
            _manager.TaskCompleted -= _manager_TaskCompleted;
            _manager.TaskAborted -= _manager_TaskAborted;
            _manager.Completed -= _manager_Completed;
            _manager.Paused -= _manager_Paused;
            _manager.TaskExceptionThrown -= _manager_TaskExceptionThrown;
            _manager.PropertyChanged -= _manager_PropertyChanged;
            
            _manager.TaskStarted += _manager_TaskStarted;
            _manager.TaskProgressChanged += _manager_TaskProgressChanged;
            _manager.TaskCompleted += _manager_TaskCompleted;
            _manager.TaskAborted += _manager_TaskAborted;
            _manager.Completed += _manager_Completed;
            _manager.Paused += _manager_Paused;
            _manager.TaskExceptionThrown += _manager_TaskExceptionThrown;
            _manager.PropertyChanged += _manager_PropertyChanged;
        }

        /// <summary>
        /// Loads the tasks.
        /// </summary>
        /// <param name="tasks">The tasks.</param>
        private void LoadTasks(TaskBase[] tasks)
        {
            dgvTasks.RowCount = tasks.Length;
            int index = 0;

            foreach (TaskBase task in tasks)
            {
                dgvTasks.Rows[index].Cells[dgcCheck.Index].Value = true;
                dgvTasks.Rows[index].Cells[dgcTask.Index].Value = task.Name;
                dgvTasks.Rows[index].Cells[dgcStatus.Index].Value = Properties.Resources.TaskStatePending;
                dgvTasks.Rows[index].Cells[dgcStatus.Index].Style.ForeColor = Color.Gainsboro;
                dgvTasks.Rows[index].Cells[dgcParallelizable.Index].Value = imlIcons.Images[task.CanBeParallelized ? 1 : 0];
                dgvTasks.Rows[index].Cells[dgcMilestone.Index].Value = imlIcons.Images[task.IsMilestone ? 2 : 3];

                _rowBindings[task.UniqueId] = index;
                _reverseRowBindings[index] = task;

                index++;
            }

            PaintParallelTasks();
        }

        #endregion Loading

        #region Initializing

        /// <summary>
        /// Initializes the localization.
        /// </summary>
        private void InitializeLocalization()
        {
            Resources.Culture = new CultureInfo("fr-FR");
            btnCheckAll.Text = Resources.btnCheckAll;
            btnRun.Text = Resources.btnRun;
            btnUncheckAll.Text = Resources.btnUncheckAll;
            dgcCheck.HeaderText = Resources.dgcCheck;
            dgcMilestone.HeaderText = Resources.dgcMilestone;
            dgcParallelizable.HeaderText = Resources.dgcParallelizable;
            dgcStatus.HeaderText = Resources.dgcStatus;
            dgcTask.HeaderText = Resources.dgcTask;
            lblTasksDescription.Text = Resources.lblTasksDescription;
            tslCpuUsage.Text = Resources.CpuUsageFormatString;
            tslMemoryUsage.Text = Resources.MemoryUsageFormatString;
            tslThreadCount.Text = Resources.ThreadsCountFormatString;
            Text = Resources.MonitoringForm;
        }

        #endregion Initializing

        #endregion Methods

        #region Performance Counters

        /// <summary>
        /// Initializes the performance counters.
        /// </summary>
        private void InitializePerformanceCounters()
        {
            string currentNamespace = Assembly.GetEntryAssembly().GetName().Name;
#if DEBUG
            currentNamespace += ".vshost";
#endif
            pfcMemoryUsage.InstanceName = currentNamespace;
            pfcCpuUsage.InstanceName = currentNamespace;
            pfcThreads.InstanceName = currentNamespace;
        }

        /// <summary>
        /// Handles the DoWork event of the bgwPerfCounters control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void bgwPerfCounters_DoWork(object sender, DoWorkEventArgs e)
        {
            while (_runPerfCounters)
            {
                bgwPerfCounters.ReportProgress(0);
                Thread.Sleep(2000);
            }
        }

        /// <summary>
        /// Handles the ProgressChanged event of the bgwPerfCounters control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param>
        private void bgwPerfCounters_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            tslMemoryUsage.Text = String.Format(Properties.Resources.MemoryUsageFormatString, pfcMemoryUsage.NextValue() / (1024 * 1024));
            tslCpuUsage.Text = String.Format(Properties.Resources.CpuUsageFormatString, pfcCpuUsage.NextValue());
            tslThreadCount.Text = String.Format(Properties.Resources.ThreadsCountFormatString, pfcThreads.NextValue());
        }

        #endregion Performance Counters

        #region Task Manager Events

        /// <summary>
        /// Handles the PropertyChanged event of the _manager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        void _manager_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            SetTasksStatus();
        }

        /// <summary>
        /// Handles the Paused event of the _manager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void _manager_Paused(object sender, PausedEventArgs e)
        {
            bool error = (e.Reason == PausedReasonType.Error);
            if (MessageBox.Show(Properties.Resources.MessageBoxTextPaused, Properties.Resources.MessageBoxCaptionPaused, MessageBoxButtons.YesNo, error ? MessageBoxIcon.Error : MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
                _manager.Resume();
        }

        /// <summary>
        /// Handles the Completed event of the _manager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void _manager_Completed(object sender, EventArgs e)
        {
            MessageBox.Show(Properties.Resources.MessageBoxTextFinished);
        }

        /// <summary>
        /// Handles the TaskExceptionThrown event of the _manager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="TaskExceptionThrownEventArgs"/> instance containing the event data.</param>
        void _manager_TaskExceptionThrown(object sender, TaskExceptionThrownEventArgs e)
        {
            SetTaskError(e.TaskId, e.InnerException);
        }

        /// <summary>
        /// Handles the TaskProgressChanged event of the _manager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="TaskProgressChangedEventArgs"/> instance containing the event data.</param>
        private void _manager_TaskProgressChanged(object sender, TaskProgressChangedEventArgs e)
        {
            SetTaskStatus(e.TaskId, String.Format(Properties.Resources.TaskStateRunningProgress, e.Progress), Color.MediumBlue);
        }

        /// <summary>
        /// Handles the TaskAborted event of the _manager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="ManageableTaskEventArgs"/> instance containing the event data.</param>
        private void _manager_TaskAborted(object sender, ManageableTaskEventArgs e)
        {
            SetTaskStatus(e.ID, Properties.Resources.TaskStateAborted, Color.DarkOrange);
        }

        /// <summary>
        /// Handles the TaskCompleted event of the _manager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="ManageableTaskEventArgs"/> instance containing the event data.</param>
        private void _manager_TaskCompleted(object sender, ManageableTaskEventArgs e)
        {
            SetTaskStatus(e.ID, Properties.Resources.TaskStateCompleted, Color.SeaGreen);
        }

        /// <summary>
        /// Handles the TaskStarted event of the _manager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="ManageableTaskEventArgs"/> instance containing the event data.</param>
        private void _manager_TaskStarted(object sender, ManageableTaskEventArgs e)
        {
            SetTaskStatus(e.ID, Properties.Resources.TaskStateRunningGeneric, Color.MediumBlue);
        }

        #endregion Task Manager Events

        #region Items update

        /// <summary>
        /// Paints the parallel tasks. Tries to paint the same color tasks that will be executed in parallel.
        /// </summary>
        private void PaintParallelTasks()
        {
            Color baseColor = Color.Ivory;
            Color alternateColor = Color.Beige;
            Color currentColor = baseColor;

            int currentGroup = -1;
            TaskBase currentTask = null;

            for (int index = 0; index < dgvTasks.RowCount; ++index)
            {
                if (_highlightParallels)
                {
                    currentTask = _reverseRowBindings[index];

                    if (currentGroup != currentTask.ParallelizationGroup || currentTask.CanBeParallelized == false)
                    {
                        currentColor = (currentColor == baseColor) ? alternateColor : baseColor;
                        currentGroup = currentTask.ParallelizationGroup;
                    }
                }
                else
                {
                    currentColor = (currentColor == baseColor) ? alternateColor : baseColor;
                }

                dgvTasks.Rows[index].Cells[dgcMilestone.Index].Style.BackColor = currentColor;
                dgvTasks.Rows[index].Cells[dgcCheck.Index].Style.BackColor = currentColor;
                dgvTasks.Rows[index].Cells[dgcParallelizable.Index].Style.BackColor = currentColor;
                dgvTasks.Rows[index].Cells[dgcTask.Index].Style.BackColor = currentColor;
                dgvTasks.Rows[index].Cells[dgcStatus.Index].Style.BackColor = currentColor;
            }
        }

        /// <summary>
        /// Sets the tasks status.
        /// </summary>
        private void SetTasksStatus()
        {
            if (InvokeRequired)
            {
                // call this method again, but on the UI thread...
                Invoke(new MethodInvoker(delegate { SetTasksStatus(); }));
            }
            else
            {
                lblTasksDescription.Text = String.Format(Resources.TasksGlobalState, _manager.RemainingTasksCount, _manager.RunningTasksCount, _manager.FinishedTasksCount, _manager.AbortedTasksCount);
            }
        }

        /// <summary>
        /// Sets the task status.
        /// </summary>
        /// <param name="taskId">The task id.</param>
        /// <param name="taskStatus">The task status.</param>
        /// <param name="itemColor">Color of the item.</param>
        private void SetTaskStatus(string taskId, string taskStatus, Color itemColor)
        {
            if (InvokeRequired)
            {
                // call this method again, but on the UI thread...
                Invoke(new MethodInvoker(delegate { SetTaskStatus(taskId, taskStatus, itemColor); }));
            }
            else
            {
                int index = _rowBindings[taskId];
                if (index >= 0 && index < dgvTasks.RowCount)
                {
                    dgvTasks.Rows[index].Cells[dgcStatus.Index].Value = taskStatus;
                    dgvTasks.Rows[index].Cells[dgcStatus.Index].Style.ForeColor = itemColor;
                }
            }
        }

        /// <summary>
        /// Sets the task status.
        /// </summary>
        /// <param name="taskId">The task id.</param>
        /// <param name="ex">The ex.</param>
        private void SetTaskError(string taskId, Exception ex)
        {
            if (InvokeRequired)
            {
                // call this method again, but on the UI thread...
                Invoke(new MethodInvoker(delegate { SetTaskError(taskId, ex); }));
            }
            else
            {
                int index = _rowBindings[taskId];
                if (index < dgvTasks.RowCount && index >= 0)
                {
                    dgvTasks.Rows[index].Cells[dgcStatus.Index].Value = Properties.Resources.TaskStateError;
                    dgvTasks.Rows[index].Cells[dgcStatus.Index].Style.ForeColor = Color.Red;
                    dgvTasks.Rows[index].Tag = ex;
                }
            }
        }

        /// <summary>
        /// Sets the select all.
        /// </summary>
        /// <param name="isChecked">if set to <c>true</c> item is checked.</param>
        private void SetCheckAll(bool isChecked)
        {
            for (int i = 0; i < dgvTasks.Rows.Count; i++)
            {
                dgvTasks.Rows[i].Cells[dgcCheck.Index].Value = isChecked;
            }
        }

        #endregion Items update

        #endregion Internals

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether parallel tasks share the same color.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if parallel tasks have the same color; otherwise, <c>false</c>.
        /// </value>
        public bool HighlightParallelTasks
        {
            get { return _highlightParallels; }
            set
            {
                _highlightParallels = value;
                PaintParallelTasks();
            }
        }

        /// <summary>
        /// Gets or sets the manager.
        /// </summary>
        /// <value>The manager.</value>
        public TaskManager Manager
        {
            get { return _manager; }
            set {
                _manager = value;
                RegisterManager();
            }
        }

        /// <summary>
        /// Sets the tasks.
        /// </summary>
        /// <value>The tasks.</value>
        public TaskBase[] Tasks
        {
            set { LoadTasks(value); }
        }

        #endregion Properties

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="MonitoringForm"/> class.
        /// </summary>
        public MonitoringForm()
        {
            InitializeComponent();
            InitializePerformanceCounters();
            InitializeLocalization();
            bgwPerfCounters.RunWorkerAsync();
        }

        #endregion Constructor

        #region GUI Events

        /// <summary>
        /// Handles the Click event of the btnCheckAll control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnCheckAll_Click(object sender, EventArgs e)
        {
            SetCheckAll(true);
        }

        /// <summary>
        /// Handles the Click event of the btnUncheckAll control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnUncheckAll_Click(object sender, EventArgs e)
        {
            SetCheckAll(false);
        }

        /// <summary>
        /// Handles the Click event of the btnRun control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnRun_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < dgvTasks.Rows.Count; i++)
            {
                // Do not change the status of unchecked tasks.
                if (Convert.ToBoolean(dgvTasks.Rows[i].Cells[dgcCheck.Index].Value).Equals(false))
                    continue;

                dgvTasks.Rows[i].Cells[dgcStatus.Index].Value = Properties.Resources.TaskStatePending;
                dgvTasks.Rows[i].Cells[dgcStatus.Index].Style.ForeColor = Color.Gainsboro;
            }

            List<TaskBase> list = new List<TaskBase>();
            foreach (int index in _reverseRowBindings.Keys)
            {
                if (Convert.ToBoolean(dgvTasks.Rows[index].Cells[dgcCheck.Index].Value).Equals(true))
                    list.Add(_reverseRowBindings[index]);
            }

            _manager.ClearTasks();
            _manager.AddGroup(list.ToArray());
            _manager.StartSequence();
        }

        /// <summary>
        /// Handles the CellDoubleClick event of the dgvTasks control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        private void dgvTasks_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            Object oTag = dgvTasks.Rows[e.RowIndex].Tag;

            if (oTag != null && oTag is Exception)
            {
                MessageBox.Show(oTag.ToString(), Properties.Resources.MessageBoxCaptionInternalTaskError, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion GUI Events
    }
}